using System;
using System.Collections.Generic;
using System.Text;
using AntEater.ComponentModel.BuildElements;
using System.Xml.XPath;
using System.Reflection;
using System.Xml;
using AntEater.ComponentModel;

namespace AntEater.Runtime.Services.Transformation
{

    /// <summary>
    /// Provides a base implementation for transformers.
    /// </summary>
    [BuildElementTransformer(typeof(UnknownBuildElement),XPathNodeType.Element)]
    internal abstract class BuildElementTransformerBase:IBuildElementTransformer
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="T:BuildElementTransformerBase"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public BuildElementTransformerBase(object context) {
            if (context == null) { throw new ArgumentNullException("context"); }
            //ToDo: Change context from object to TransformerContext

            this._context = context as TransformerContext;
        }

        #region IBuildElementTransformer Members

        public event GetChildElementsXml GetChildElementsXml;

        /// <summary>
        /// Transforms an <c>BuildElement</c> into XML.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        /// <param name="writer">The writer.</param>
        /// <param name="errors">The errors.</param>
        public abstract void GetXml(IBuildElement buildElement, XmlWriter writer, List<TransformationError> errors);

        /// <summary>
        /// Gets an <c>BuildElement</c> from XML.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="buildFileDefinition">The build file definition.</param>
        /// <param name="errors">The errors.</param>
        /// <returns></returns>
        public abstract IBuildElement GetBuildElement(XPathNavigator node, BuildFileDefinition buildFileDefinition, List<TransformationError> errors);

        private TransformerContext _context;

        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>The context.</value>
        public TransformerContext Context {
            get { return _context; }
        }

        #endregion

        private Type _buildElementType;

        /// <summary>
        /// Gets the type of the build element.
        /// </summary>
        /// <value>The type of the build element.</value>
        public Type BuildElementType {
            get {
                if (_buildElementType == null) {

                    BuildElementTransformerAttribute attribute
                        = ComponentModel.Utilities.GetTypeAttributeFromObject<BuildElementTransformerAttribute>(this);

                    if (attribute != null) {
                        _buildElementType = attribute.BuildElementType;
                    }
                }
                return _buildElementType;
            }
        }

        /// <summary>
        /// Gets the name of the build element property.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        protected string GetBuildElementPropertyName(string propertyName)
        {
            string buildElementPropertyName = "";

            BuildElementPropertyAttribute attribute
                = ComponentModel.Utilities.GetPropertyAttributeFromType<BuildElementPropertyAttribute>(BuildElementType, propertyName);

            if (attribute != null)
            {
                buildElementPropertyName = attribute.Name;
            }

            return buildElementPropertyName;
        }

        /// <summary>
        /// Fires the GetChildElementsXml Event.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="writer">The writer.</param>
        protected void OnGetChildElementsXml(IBuildElement element, XmlWriter writer) {
            if (this.GetChildElementsXml != null) {
                GetChildElementsXml.Invoke(element, writer);
            }
        }
    }
}
